#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
Modbus RTU服务器示例
基于pymodbus 3.11.2实现，支持多从站设备模拟
"""

import random
import time
import logging
import threading
from datetime import datetime

from pymodbus.server import StartSerialServer
from pymodbus.datastore import ModbusServerContext, ModbusDeviceContext
from pymodbus.datastore import ModbusSequentialDataBlock
from pymodbus import ModbusDeviceIdentification, FramerType

# 日志配置
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# --------------------------
# 参数配置
# --------------------------
PORT = '/dev/tty.usbserial-1120'  # 串口端口
BAUDRATE = 9600                    # 波特率
NUM_SLAVES = 10                    # 从站数量
REGISTER_COUNT = 20                # 每个从站的寄存器数量
UPDATE_INTERVAL = 2                # 寄存器更新间隔（秒）

# --------------------------
# 创建多从站数据存储区
# --------------------------
def create_slave_context(register_count: int = REGISTER_COUNT):
    """创建单个从站的数据存储区"""
    return ModbusDeviceContext(
        di=ModbusSequentialDataBlock(0, [0] * 10),        # 离散输入
        co=ModbusSequentialDataBlock(0, [0] * 10),        # 线圈
        hr=ModbusSequentialDataBlock(0, [0] * register_count),  # 保持寄存器
        ir=ModbusSequentialDataBlock(0, [0] * 10),        # 输入寄存器
    )

# 创建多从站上下文
slave_contexts = {
    slave_id: create_slave_context() for slave_id in range(1, NUM_SLAVES + 1)
}
context = ModbusServerContext(devices=slave_contexts, single=False)

# --------------------------
# 创建设备标识
# --------------------------
identity = ModbusDeviceIdentification()
identity.VendorName = 'IoT-Util'
identity.ProductCode = 'ModbusRTUSim'
identity.ProductName = 'Modbus RTU Multi-Slave Simulator'
identity.ModelName = 'RTU Model X'
identity.MajorMinorRevision = '1.0'

# --------------------------
# 定时更新从站寄存器值的线程
# --------------------------
def update_registers(server_ref):
    """更新寄存器数据的线程函数"""
    logger.info(f"🔄 [{datetime.now().strftime('%H:%M:%S')}] 开始更新所有从站寄存器...")

    while getattr(server_ref, 'running', True):
        try:
            for slave_id, slave_context in slave_contexts.items():
                # 生成随机int16格式的数值
                values = [random.randint(0, 32767) for _ in range(REGISTER_COUNT)]
                # 写入保持寄存器地址 0~REGISTER_COUNT-1
                slave_context.setValues(3, 0, values)
                logger.info(f"从站{slave_id}: HR[0-{REGISTER_COUNT-1}]={values}")
            time.sleep(UPDATE_INTERVAL)
        except Exception as e:
            logger.error(f"❌ 寄存器更新错误: {e}")
            time.sleep(UPDATE_INTERVAL)

# --------------------------
# 启动 RTU 服务器
# --------------------------
def start_rtu_server(
    port: str = PORT,
    baudrate: int = BAUDRATE,
    timeout: float = 1.0
):
    """启动RTU服务器"""
    logger.info(f"🚀 启动Modbus RTU服务器: {port}@{baudrate}bps")

    # 创建服务器引用用于控制停止
    server_ref = type('ServerRef', (), {'running': True})()

    # 启动后台线程更新寄存器
    update_thread = threading.Thread(target=update_registers, args=(server_ref,), daemon=True)
    update_thread.start()

    try:
        # 启动服务器（阻塞运行）
        logger.info(f"✅ RTU服务器已在 {port}@{baudrate} 启动，等待连接...")
        StartSerialServer(
            context=context,
            identity=identity,
            port=port,
            baudrate=baudrate,
            framer=FramerType.RTU,
            timeout=timeout
        )
    except KeyboardInterrupt:
        logger.info("🛑 接收到停止信号")
    except Exception as e:
        logger.error(f"❌ 服务器运行错误: {e}")
    finally:
        # 停止服务器
        server_ref.running = False
        logger.info("🔌 RTU服务器已关闭")

# --------------------------
# 主程序入口
# --------------------------
if __name__ == "__main__":
    start_rtu_server()
